// Copyright (c) 2014-07-31
// author: yuarmy@126.com

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include "shdevmag.h"
#include "uart.h"
#include "shsocket.h"

int32_t InitSH(SHDEVMANAGE_T *shdev) {
  int32_t ret;
  ret = InitSocket(9734, 5, shdev);

  if (ret < 0) {
    goto fail;
  }

  FD_ZERO(&shdev->readfds);
  if ( shdev->server_sockfd > 0) {
    FD_SET(shdev->server_sockfd, &shdev->readfds);
  }

  ret = InitUartTerm(shdev);
  if ( shdev->uart_fd > 0) {
    FD_SET(shdev->uart_fd, &shdev->readfds);
  }
fail:
  return ret;
}
/*  If it isn't the server, it must be client activity.
            If close is received, the client has gone away and we remove it from the descriptor set.
            Otherwise, we 'serve' the client as in the previous examples.  */
int32_t TranfData(SHDEVMANAGE_T *shdev, int fd) {
  int32_t ret, len, i;
  int nread;
  uint8_t  buff[SH_MAX_PATH];

  fprintf(stderr, "%s %d\n", __FILE__, __LINE__);
  ret = 0;
  ioctl(fd, FIONREAD, &nread);
  if(nread == 0) {
    close(fd);
    FD_CLR(fd, &shdev->readfds);
    printf("removing client on fd %d\n", fd);
    if (shdev->client_sockfd == fd) {
      shdev->client_sockfd = 0;
    } else if (shdev->uart_fd == fd) {
      shdev->uart_fd = 0;
    } else if (shdev->test_sockfd == fd) {
      shdev->test_sockfd = 0;
    }
    ret = 0;
  } else {
    len = read(fd, buff, SH_MAX_PATH);
    if (len < 0) {
      fprintf(stderr, "%s %d it is error: %d, read is fail.\n",
              __FILE__, __LINE__, errno);
      ret = -1;
      goto fail;
    }

    // printf("receiving client on fd %d, %c\n", fd, buff[0]);
    if (fd == shdev->uart_fd) {
      if (shdev->client_sockfd > 0) {
        ret = WriteToSocket(shdev, buff, len);
      } else {
	fprintf(stderr, "%s %d  it is still not open client socket.\n",
	    __FILE__, __LINE__);
	for (i = 0; i < len; i++) {
	  fprintf(stderr, "%x ", buff[i]);
	}
	fprintf(stderr, "\n");
        ret = 0;
      }
    } else if (fd == shdev->client_sockfd) {
      if (shdev->uart_fd > 0) {
        ret = WriteToUart(shdev, buff, len);
        if (ret < 0) {
	  WriteToSocket(shdev, buff, len);
	  fprintf(stderr, "%s %d  it is fail to write uart.\n",
	      __FILE__, __LINE__);
	  ret = 0;
        }
      } else {
        WriteToSocket(shdev, buff, len);
        fprintf(stderr, "%s %d  it is fail to write uart.\n",
                __FILE__, __LINE__);
        ret = 0;
      }
    } else if (fd == shdev->test_sockfd) {
      if (shdev->client_sockfd > 0) {
        ret = WriteToSocket(shdev, buff, len);
        if (ret < 0) {
          shdev->client_state = -1;
          ret = 0;
        } else if (shdev->client_state < 0) {
          shdev->client_state = 0;
        }
        if (ret != len) {
          fprintf(stderr, "%s %d  writte len is less than read len.\n",
                  __FILE__, __LINE__);
        }
      }
    }
  }
fail:
  fprintf(stderr, "%s %d\n", __FILE__, __LINE__);
  return ret;
}
void quit(int sig) {
}

int32_t IdentifyFD(SHDEVMANAGE_T *shdev, fd_set *testfds) {
  int32_t ret = 0;
  int fd;
  /*  Once we know we've got activity,
      we find which descriptor it's on by checking each in turn using FD_ISSET.  */

  fprintf(stderr, "%s %d\n", __FILE__, __LINE__);
  for(fd = 0; fd < FD_SETSIZE; fd++) {
    if(FD_ISSET(fd, testfds)) {
      if(fd == shdev->server_sockfd) {
        ret = OpenNewSocket(shdev);
        if (ret >= 0) {
          FD_SET(ret, &shdev->readfds);
        }
      } else {
        ret = TranfData(shdev, fd);
      }
      if (ret < 0) {
        break;
      }
    }
  } //for

  fprintf(stderr, "%s %d\n", __FILE__, __LINE__);
  return ret;
}
int32_t WaitReadDev(SHDEVMANAGE_T *shdev, fd_set *testfds) {
  int32_t ret;
  printf("device moniter server waiting\n");
  ret = select(FD_SETSIZE, testfds, NULL, NULL, NULL);
  fprintf(stderr, "%s %d\n", __FILE__, __LINE__);

  if(ret < 1) {
    if (EINTR != errno) {
      fprintf(stderr, "%s %d it is error: %d, so fail to select socket!\n",
              __FILE__, __LINE__, errno);
    }
    ret = -1;
  }
  return ret;
}
 
int32_t process(SHDEVMANAGE_T *shdev) {
  int32_t ret;
  fd_set testfds;
  struct sigaction act;
  act.sa_handler = quit;
  sigemptyset(&act.sa_mask);
  act.sa_flags = 0;

  sigaction(SIGINT, &act, 0);
  while(1) {
    testfds = shdev->readfds;

    ret = WaitReadDev(shdev, &testfds);
    if (ret < 0) {
      if (EINTR != errno) {
	fprintf(stderr, "%s %d it is error: %d, so fail to wait read dev!\n",
	    __FILE__, __LINE__, errno);
      }
      break;
    }

    ret = IdentifyFD(shdev, &testfds);
    if (ret < 0) {
      fprintf(stderr, "%s %d it is error: %d, so fail to wait read dev!\n",
              __FILE__, __LINE__, errno);
      break;
    }
  } //while

  if (shdev->client_sockfd > 0) {
    close(shdev->client_sockfd);
  }
  if (shdev->server_sockfd > 0) {
    close(shdev->server_sockfd);
  }
  printf("process is done.\n");
  return ret;
}
